//
//  FirmwareUpgradeViewModel.swift
//  EarbudsCommon
//
//  Created by 李然 on 2024/5/8.
//

import Foundation
import RxCocoa
import RxSwift
import CRPEarPhone
import MoyoungLib

public enum UpgradeState {
    case normal
    case updating
    case updateSuccess
    case updateFailure
}

public class FirmwareUpgradeViewModel: ViewModel, ViewModelType {
    
    let newVersion: Version
    
    public init(newVersion: Version) {
        self.newVersion = newVersion
    }
    
    public struct Input {
        public let buttonClick: Observable<Void>
        public init(buttonClick: Observable<Void>) {
            self.buttonClick = buttonClick
        }
    }
    
    public struct Output {
        public let state: Driver<UpgradeState>
        public let currentVersion: Driver<String>
        public let upgateProgress: Driver<Double>
        public let popAction: Driver<Void>
    }
    
    public func transform(input: Input) -> Output {
        
        let upgradeState = BehaviorRelay(value: UpgradeState.normal)
        let currentVersion = BehaviorRelay(value: "")
        
        let newVersion = PublishSubject<(version: String?, message: String?)>()
        let upgateProgress = PublishSubject<Double>()
        let pop = PublishRelay<Void>()
        
        BluetoothManager.shared.connectState
            .map{ _ in Device.current()?.version }
            .filterNil()
            .bind(to: currentVersion).disposed(by: rx.disposeBag)
        
        var updateingBag = DisposeBag()
        
        input.buttonClick.subscribe(onNext: { [weak self] _ in
            guard let `self` = self else { return }
            
            if upgradeState.value == .updateSuccess {
                pop.accept(())
                return
            }
            
            updateingBag = DisposeBag()
            
            upgradeState.accept(.updating)
            upgateProgress.onNext(0)
            
            let version = self.newVersion
            
            let api = DownloadAPI.downloadFirmware(url: version.fileUrl, version.md5)
            ApiFetcher.rxDownload(api)
                .subscribe { response in
                    if let p = response.progress {
                        upgateProgress.onNext(p.fractionCompleted / 2)
                    }
                } onError: { error in
                    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                        log.debug(error.localizedDescription)
                        upgradeState.accept(.updateFailure)
                        upgradeState.accept(.normal)
                    }
                } onCompleted: {
                    
                    BluetoothManager.shared.startUpgradeFromFile(path: api.localLocation.path)
                        .subscribe(onNext: { result in
                            upgateProgress.onNext(0.5 + Double(result.progress)/100.0/2)
                            if result.state == .completed {
                                do {
                                    try FileManager.default.removeItem(at: api.localLocation)
                                } catch {
                                    log.debug("删除固件文件失败")
                                }
                                let device = Device.current()
                                device?.version = version.version
                                device?.save()
                                upgradeState.accept(.updateSuccess)
                            } else if result.state == .failed {
                                do {
                                    try FileManager.default.removeItem(at: api.localLocation)
                                } catch {
                                    log.debug("删除固件文件失败")
                                }
                                upgradeState.accept(.updateFailure)
                                upgradeState.accept(.normal)
                            }
                        }).disposed(by: updateingBag)
                    
                }.disposed(by: updateingBag)
            
        }).disposed(by: rx.disposeBag)
        
        return Output(state: upgradeState.asDriver().distinctUntilChanged(),
                      currentVersion: currentVersion.asDriver(),
                      upgateProgress: upgateProgress.asDriverOnErrorJustComplete(),
                      popAction: pop.asDriverOnErrorJustComplete())
    }
}
